package com.java.gps.utils;

import com.java.gps.domain.GpsInfo;

/**
 * @author jl
 * Created on 2020/7/20
 * Gps工具类
 * GPGSV：可见卫星信息
 * GPGLL：地理定位信息
 * GPRMC：推荐最小定位信息
 * GPVTG：地面速度信息
 * GPGGA：GPS定位信息
 * GPGSA：当前卫星信息
 */
public class GpsUtil {

    public static void main(String[] args) {

        String code = "$GPRMC,025523.00,A,3000.508632,N,12202.843943,E,0.0,315.9,160418,4.3,W,A*2C\\r";
        GpsInfo gpsInfo = GPRMCAnalysis(code);
        System.out.println(gpsInfo);
    }

    /**
     * GNRMC解析[北斗]
     *
     * @param code 原始字符串
     * @return 北斗定位信息
     */
    public static GpsInfo GNRMCAnalysis(String code) {
        GpsInfo GpsInfo = null;
        code = code.replace("\r", "\n");
        code = code.replace("\n\n", "\n");
        String[] strtemp = code.split("\n");
        for (int i = 0; i < strtemp.length; i++) {
            String[] strtemp1 = strtemp[i].split(",");
            if (strtemp1.length >= 12) {
                if (strtemp1[0].equals("$GNRMC")) {
                    String gpsStatus = strtemp1[2];
                    String course = strtemp1[8];
                    String speed = strtemp1[7];
                    String latitude = strtemp1[3].equals("") ? "" : gpsTransforming(strtemp1[3]).toString();
                    String longitude = strtemp1[5].equals("") ? "" : gpsTransforming(strtemp1[5]).toString();
                    String gpsTime = strtemp1[9].equals("") ? "" : "20" + strtemp1[9].substring(4, 2) + "-" + strtemp1[9].substring(2, 2) + "-" + strtemp1[9].substring(0, 2) + " " + strtemp1[1].substring(0, 2) + ":" + strtemp1[1].substring(2, 2) + ":" + strtemp1[1].substring(4, 2);
                    GpsInfo = new GpsInfo(longitude, latitude, speed, gpsStatus, gpsTime, course, null);
                }
            }
        }
        return GpsInfo;
    }

    /**
     * GPRMC字符串解析[GPS]
     *
     * @param code 原始字符串
     * @return GPS定位信息
     */
    public static GpsInfo GPRMCAnalysis(String code) {
        GpsInfo GpsInfo = null;
        if (code != null && code.length() > 0 && code.startsWith("$GPRMC") && checksum(code) && code.split(",")[2].equals("A")) {
            code = code.contains("\r") ? code.substring(0, code.indexOf("\r")) : code;
            String[] seg = code.split(",");
            if (seg.length >= 12) {
                String gpsStatus = seg[2];
                String course = seg[8];
                String speed = seg[7];
                String latitude = seg[3].equals("") ? "" : getZf(seg[4], gpsTransforming(seg[3]).toString());
                String longitude = seg[5].equals("") ? "" : getZf(seg[6], gpsTransforming(seg[5]).toString());
                String gpsTime = seg[9].equals("") ? "" : "20" + seg[9].substring(4, 6) + "-" + seg[9].substring(2, 4) + "-" + seg[9].substring(0, 2) + " " + seg[1].substring(0, 2) + ":" + seg[1].substring(2, 4) + ":" + seg[1].substring(4, 6);
                String magneticDeclination = seg[10].equals("") ? "" : getZf(seg[11], seg[10]);
                GpsInfo = new GpsInfo(longitude, latitude, speed, gpsStatus, gpsTime, course, magneticDeclination);
            }
        }
        return GpsInfo;
    }

    /**
     * 取正负
     */
    private static String getZf(String declination, String value) {
        String returnValue = "";
        if (declination != null && declination.length() > 0) {
            if (declination.equals("W")) {
                returnValue += "-";
            } else if (declination.equals("S")) {
                returnValue += "-";
            }
        }
        if (Double.parseDouble(value) == 0.0) {
            returnValue = "";
        }
        returnValue += value;
        return returnValue;
    }

    /**
     * 降度分格式经纬度转换为小数经纬度
     *
     * @param value 度分经纬度
     * @return 小数经纬度
     */
    private static Double gpsTransforming(String value) {
        double ret = 0.0;
        String[] tempStr = value.split("[.]");
        String x = tempStr[0].substring(0, tempStr[0].length() - 2);
        String y = tempStr[0].substring(tempStr[0].length() - 2) + "." + tempStr[1];
        ret = Double.parseDouble(x) + Double.parseDouble(y) / 60;
        return ret;
    }

    /**
     * 和校验
     *
     * @param sentence 原始字符串
     * @return 是否校验成功
     */
    private static boolean checksum(String sentence) {
        short checksum = 0;

        int checksumIndex = sentence.lastIndexOf('*');

        if (sentence.length() < checksumIndex + 3)
            return false;

        checksum = getChecksum(sentence);

        short high = (short) ((checksum & 0x00F0) >> 4);
        short low = (short) (checksum & 0x000F);

        return (high == hexValue(sentence.charAt(checksumIndex + 1))) &&
                (low == hexValue(sentence.charAt(checksumIndex + 2)));
    }

    private static short getChecksum(String sentence) {
        short checksum = 0;

        int checksumIndex = sentence.lastIndexOf('*');

        if (checksumIndex > 0 && sentence.length() < checksumIndex + 3)
            return 0;

        if (checksumIndex < 0)
            checksumIndex = sentence.length();

        for (int i = 1; i < checksumIndex; i++) {
            checksum ^= (short) sentence.charAt(i);
        }

        return checksum;
    }

    private static short hexValue(char c) {
        switch (c) {
            case 'A':
            case 'a':
                return 10;
            case 'B':
            case 'b':
                return 11;
            case 'C':
            case 'c':
                return 12;
            case 'D':
            case 'd':
                return 13;
            case 'E':
            case 'e':
                return 14;
            case 'F':
            case 'f':
                return 15;

            case '1':
                return 1;
            case '2':
                return 2;
            case '3':
                return 3;
            case '4':
                return 4;
            case '5':
                return 5;
            case '6':
                return 6;
            case '7':
                return 7;
            case '8':
                return 8;
            case '9':
                return 9;

            default:
                return 0;
        }
    }

}
